{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Pandas 设置索引详解\n",
        "\n",
        "本教程详细介绍 Pandas 中 `.set_index()` 方法的用法，包括单列索引、多列索引、多级索引等操作。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n",
        "\n",
        "# 创建示例数据集\n",
        "np.random.seed(42)\n",
        "data = {\n",
        "    'ID': [1001, 1002, 1003, 1004, 1005, 1006],\n",
        "    '姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八'],\n",
        "    '年龄': [25, 30, 35, 28, 32, 27],\n",
        "    '部门': ['技术', '销售', '技术', '人事', '销售', '技术'],\n",
        "    '工资': [8000, 12000, 15000, 6000, 11000, 9000],\n",
        "    '入职日期': pd.date_range('2020-01-01', periods=6, freq='ME')\n",
        "}\n",
        "\n",
        "df = pd.DataFrame(data)\n",
        "print(\"原始数据集：\")\n",
        "df\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## `.set_index()` - 设置索引\n",
        "\n",
        "`.set_index()` 方法用于将 DataFrame 中的一列或多列设置为行索引。\n",
        "\n",
        "**语法：** `DataFrame.set_index(keys, drop=True, append=False, inplace=False, verify_integrity=False)`\n",
        "\n",
        "**主要参数：**\n",
        "- `keys`: 要设置为索引的列名或列名列表\n",
        "- `drop`: 布尔值，默认为 True，表示是否删除原来的列\n",
        "- `append`: 布尔值，默认为 False，表示是否将新的索引追加到现有索引（创建多级索引）\n",
        "- `inplace`: 布尔值，默认为 False，表示是否在原 DataFrame 上修改\n",
        "- `verify_integrity`: 布尔值，默认为 False，表示是否检查新索引是否有重复值\n",
        "\n",
        "**适用场景：**\n",
        "- 将某一列作为行标识符\n",
        "- 创建多级索引（层次化索引）\n",
        "- 根据业务需求重新组织数据\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 1.1 设置单列索引（默认删除原列）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 将 'ID' 列设置为索引（默认 drop=True，会删除原列）\n",
        "df1 = df.set_index('ID')\n",
        "print(\"=== 将 ID 列设置为索引（drop=True，删除原列）===\")\n",
        "df1\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 1.2 设置索引但保留原列\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 将 'ID' 列设置为索引，但保留原列（drop=False）\n",
        "df2 = df.set_index('ID', drop=False)\n",
        "print(\"=== 将 ID 列设置为索引（drop=False，保留原列）===\")\n",
        "df2\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 1.3 使用 inplace 参数直接修改原 DataFrame\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建副本用于演示\n",
        "df3 = df.copy()\n",
        "\n",
        "# 使用 inplace=True 直接修改原 DataFrame\n",
        "df3.set_index('ID', inplace=True)\n",
        "print(\"=== 使用 inplace=True 直接修改原 DataFrame ===\")\n",
        "print(\"df3 已被修改：\")\n",
        "df3\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 1.4 设置多列索引（创建多级索引）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 将 '部门' 和 '姓名' 两列设置为多级索引\n",
        "df4 = df.set_index(['部门', '姓名'])\n",
        "print(\"=== 创建多级索引（部门 -> 姓名）===\")\n",
        "df4\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 1.5 追加索引（append=True）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 先设置一个索引\n",
        "df5 = df.set_index('ID')\n",
        "\n",
        "# 再追加另一个索引（append=True）\n",
        "df5 = df5.set_index('部门', append=True)\n",
        "print(\"=== 追加索引（先设置 ID，再追加 部门）===\")\n",
        "df5\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 1.6 验证索引唯一性（verify_integrity=True）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建包含重复值的数据\n",
        "df_duplicate = pd.DataFrame({\n",
        "    'ID': [1001, 1001, 1002, 1003],\n",
        "    '姓名': ['张三', '李四', '王五', '赵六'],\n",
        "    '年龄': [25, 30, 35, 28]\n",
        "})\n",
        "\n",
        "print(\"原始数据（包含重复的ID）：\")\n",
        "print(df_duplicate)\n",
        "print(\"\\n=== 尝试将重复的 ID 设置为索引 ===\")\n",
        "\n",
        "try:\n",
        "    # verify_integrity=False（默认），允许重复索引\n",
        "    df6 = df_duplicate.set_index('ID', verify_integrity=False)\n",
        "    print(\"verify_integrity=False 时，允许重复索引：\")\n",
        "    print(df6)\n",
        "    print(\"\\n索引值：\", df6.index.tolist())\n",
        "except ValueError as e:\n",
        "    print(f\"错误：{e}\")\n",
        "\n",
        "print(\"\\n\" + \"=\"*50)\n",
        "\n",
        "try:\n",
        "    # verify_integrity=True，检查索引唯一性\n",
        "    df7 = df_duplicate.set_index('ID', verify_integrity=True)\n",
        "    print(\"verify_integrity=True 时，不允许重复索引\")\n",
        "except ValueError as e:\n",
        "    print(f\"验证失败（这是预期的）：{e}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 索引操作技巧\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.1 查看当前索引信息\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建示例\n",
        "df_info = df.set_index(['部门', '姓名'])\n",
        "\n",
        "print(\"=== 索引基本信息 ===\")\n",
        "print(f\"索引名称: {df_info.index.names}\")\n",
        "print(f\"索引级别数: {df_info.index.nlevels}\")\n",
        "print(f\"索引值: {df_info.index.tolist()[:3]}...\")  # 只显示前3个\n",
        "print(f\"索引是否唯一: {df_info.index.is_unique}\")\n",
        "print(f\"索引类型: {type(df_info.index)}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.2 重命名索引\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 重命名索引\n",
        "df_rename = df.set_index('ID')\n",
        "df_rename.index.name = '员工编号'\n",
        "print(\"=== 重命名索引后 ===\")\n",
        "df_rename\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.3 重命名多级索引\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 重命名多级索引\n",
        "df_multi_rename = df.set_index(['部门', '姓名'])\n",
        "df_multi_rename.index.names = ['所属部门', '员工姓名']\n",
        "print(\"=== 重命名多级索引后 ===\")\n",
        "df_multi_rename\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.4 通过索引访问数据\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 设置单级索引\n",
        "df_access = df.set_index('ID')\n",
        "\n",
        "print(\"=== 通过索引访问单行 ===\")\n",
        "print(df_access.loc[1001])\n",
        "\n",
        "print(\"\\n=== 通过索引访问多行 ===\")\n",
        "print(df_access.loc[[1001, 1003, 1005]])\n",
        "\n",
        "print(\"\\n=== 通过索引切片 ===\")\n",
        "print(df_access.loc[1001:1004])\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 设置多级索引\n",
        "df_access_multi = df.set_index(['部门', '姓名'])\n",
        "\n",
        "print(\"=== 通过多级索引访问数据 ===\")\n",
        "print(\"访问单个值：\")\n",
        "print(df_access_multi.loc[('技术', '张三')])\n",
        "\n",
        "print(\"\\n访问第一级索引：\")\n",
        "print(df_access_multi.loc['技术'])\n",
        "\n",
        "print(\"\\n使用 xs 方法访问：\")\n",
        "print(df_access_multi.xs('技术', level='部门'))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 注意事项和最佳实践\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 3.1 索引的选择原则\n",
        "\n",
        "1. **唯一性**：索引值应该尽量唯一，这样可以使用 `verify_integrity=True` 来确保数据质量\n",
        "2. **稳定性**：索引值不应该频繁变化，应该选择相对稳定的列作为索引\n",
        "3. **业务意义**：选择在业务查询中经常使用的列作为索引，如 ID、日期等\n",
        "4. **数据类型**：索引通常是字符串或整数，避免使用浮点数作为索引（可能会有精度问题）\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 3.2 inplace 参数的使用\n",
        "\n",
        "- **inplace=False（默认）**：返回新对象，原对象不变。适合需要保留原数据的场景\n",
        "- **inplace=True**：直接修改原对象，不返回新对象。适合数据量大或确定不需要原数据的场景\n",
        "\n",
        "**建议**：在数据处理流水线中，根据实际情况选择。如果需要进行多步操作，建议使用 `inplace=False` 以便随时查看中间结果。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 3.3 性能优化建议\n",
        "\n",
        "1. **索引排序**：使用 `sort_index()` 对索引进行排序可以提高查询性能\n",
        "2. **索引合并**：使用索引进行 `join()` 操作比 `merge()` 更高效\n",
        "3. **合理选择索引列**：选择查询频率高的列作为索引，可以显著提升查询速度\n",
        "\n",
        "**示例：**\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 性能优化示例：排序索引\n",
        "df_perf = df.set_index('ID').sort_index()\n",
        "print(\"=== 对索引进行排序 ===\")\n",
        "df_perf\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 总结\n",
        "\n",
        "**`.set_index()` 方法的核心要点：**\n",
        "- 用于将列转换为行索引\n",
        "- 支持单列和多列索引（创建多级索引）\n",
        "- 可以控制是否删除原列（`drop` 参数）\n",
        "- 可以追加到现有索引（`append=True`）\n",
        "- 可以验证索引唯一性（`verify_integrity=True`）\n",
        "\n",
        "**使用场景：**\n",
        "- 数据查询和分析时，将常用列设置为索引以提高查询效率\n",
        "- 数据合并时，使用索引可以简化操作并提高性能\n",
        "- 分组统计时，使用多级索引可以更好地组织数据结构\n",
        "\n",
        "掌握索引的设置是 Pandas 数据处理的重要技能，能够让你的数据分析工作更加高效！\n"
      ]
    }
  ],
  "metadata": {
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
